package com.ruoyi.system.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.TbDownloadToken;
import com.ruoyi.system.downloadexception.DownloadInterruptedException;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.FileStorageService;
import com.ruoyi.system.service.IAPPOrDataPackageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@Slf4j
@Service
public class APPOrDataPackageServiceImpl implements IAPPOrDataPackageService {

    @Autowired
    private TbDeviceMapper deviceMapper;

    @Autowired
    private TbAppCatalogMapper appCatalogMapper;

    @Autowired
    private TbDeviceAppsMapper deviceAppsMapper;

    @Autowired
    private TbDataPackageMapper dataPackageMapper;


    @Autowired
    private TbAppVersionMapper appVersionMapper;


    @Autowired
    private TbDeviceAppLogsMapper deviceAppLogMapper;


    @Autowired
    private TbDeviceDataPackagesMapper deviceDataPackagesMapper;


    @Autowired
    private TbDeviceDataPackageLogsMapper deviceDataPackageLogsMapper;


    @Autowired
    private TbDownloadTokenMapper downloadTokenMapper;

    @Autowired
    private FileStorageService fileStorageService;


    @Value("${file.storage.package}")
    private String localStoragePath;


    @Override
    public ResponseEntity<StreamingResponseBody> download(String devSN, String downToken, HttpServletRequest request) {

        long start = 0;
        long end = -1;
        boolean hasRange = false;

        try {
            // 参数验证
            if (StringUtils.isBlank(devSN) || StringUtils.isBlank(downToken)) {
                return createErrorResponse(HttpStatus.BAD_REQUEST, "参数不能为空");
            }

            if (deviceMapper.validateDevice(devSN) == null) {
                return createErrorResponse(HttpStatus.FORBIDDEN, "设备无效!");
            }

            TbDownloadToken token = downloadTokenMapper.selectValidByToken(downToken);
            if (token == null || StringUtils.isBlank(token.getFileUrl())) {
                return createErrorResponse(HttpStatus.FORBIDDEN, "无效的下载令牌");
            }

            String fileUrl = token.getFileUrl();

            // 处理HEAD请求
            if ("HEAD".equalsIgnoreCase(request.getMethod())) {
                return handleHeadRequest(fileUrl);
            }

            // 解析Range头（保持断点续传功能）
            String rangeHeader = request.getHeader("Range");
            if (StringUtils.isNotBlank(rangeHeader) && rangeHeader.startsWith("bytes=")) {
                try {
                    String rangeValue = rangeHeader.substring(6);
                    String[] ranges = rangeValue.split("-");

                    if (ranges.length >= 1 && !ranges[0].isEmpty()) {
                        start = Long.parseLong(ranges[0]);
                        if (ranges.length > 1 && !ranges[1].isEmpty()) {
                            end = Long.parseLong(ranges[1]);
                        } else {
                            end = -1;
                        }
                        hasRange = true;
                    }
                } catch (NumberFormatException e) {
                    return createErrorResponse(HttpStatus.BAD_REQUEST, "Invalid Range format");
                }
            }

            // 根据文件URL类型选择不同的下载方式
            if (isLocalFile(fileUrl)) {
                return handleLocalFileDownload(fileUrl, request, start, end, hasRange);
            } else if (isHttpUrl(fileUrl)) {
                return handleRemoteFileDownload(fileUrl, request, start, end, hasRange);
            } else {
                return createErrorResponse(HttpStatus.BAD_REQUEST, "不支持的文件路径格式");
            }

        } catch (Exception e) {
            log.error("文件下载失败", e);
            return createErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, "下载失败: " + e.getMessage());
        }
    }

    /**
     * 创建错误响应
     */
    private ResponseEntity<StreamingResponseBody> createErrorResponse(HttpStatus status, String message) {
        return ResponseEntity.status(status)
                .header("Content-Type", "text/plain;charset=UTF-8")
                .body(outputStream -> outputStream.write(message.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 处理HEAD请求
     */
    private ResponseEntity<StreamingResponseBody> handleHeadRequest(String fileUrl) throws IOException {
        long fileSize = getFileSize(fileUrl);
        if (fileSize == -1) {
            return createErrorResponse(HttpStatus.NOT_FOUND, "文件不存在");
        }

        String fileName = getFileNameFromUrl(fileUrl);
        String safeFileName = getSafeFileName(fileName);

        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/octet-stream");
        headers.set("Content-Disposition", "attachment; filename=\"" + safeFileName + "\"");
        headers.set("Content-Length", String.valueOf(fileSize));
        headers.set("Accept-Ranges", "bytes");
        headers.set("Cache-Control", "no-cache");

        return new ResponseEntity<>(null, headers, HttpStatus.OK);
    }

    /**
     * 处理本地文件下载
     */
    private ResponseEntity<StreamingResponseBody> handleLocalFileDownload(String filePath, HttpServletRequest request,
                                                                          long start, long end, boolean hasRange) throws IOException {
        // 路径处理
        if (!filePath.startsWith(localStoragePath)) {
            filePath = localStoragePath + File.separator + filePath;
        }

        File file = new File(filePath);
        if (!file.exists() || !file.isFile()) {
            return createErrorResponse(HttpStatus.NOT_FOUND, "文件不存在");
        }

        long fileSize = file.length();
        String fileName = file.getName();

        // 处理Range请求验证
        if (hasRange) {
            if (end == -1) {
                end = fileSize - 1;
            }
            if (start >= fileSize || end >= fileSize || start > end) {
                return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
                        .header("Content-Range", "bytes */" + fileSize)
                        .body(null);
            }
        }

        // 创建响应头
        HttpHeaders headers = createDownloadHeaders(fileName, fileSize, start, end, hasRange);

        // 创建响应体
        long finalEnd = end;
        StreamingResponseBody responseBody = outputStream -> {
            try (RandomAccessFile randomFile = new RandomAccessFile(file, "r")) {
                if (hasRange) {
                    streamFileRange(randomFile, outputStream, start, finalEnd, request);
                } else {
                    streamFullFile(randomFile, outputStream, request);
                }
            } catch (DownloadInterruptedException e) {
                handleDownloadInterruption(outputStream, e.getBytesTransferred(), e.getMessage());
            }
        };

        return new ResponseEntity<>(responseBody, headers, hasRange ? HttpStatus.PARTIAL_CONTENT : HttpStatus.OK);
    }

    /**
     * 处理远程文件下载
     */
    private ResponseEntity<StreamingResponseBody> handleRemoteFileDownload(String fileUrl, HttpServletRequest request,
                                                                           long start, long end, boolean hasRange) throws IOException {
        URL url = new URL(fileUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setConnectTimeout(30000);
        connection.setReadTimeout(300000);

        // 设置Range头（保持断点续传）
        if (hasRange) {
            String rangeHeaderValue = end != -1 ?
                    "bytes=" + start + "-" + end : "bytes=" + start + "-";
            connection.setRequestProperty("Range", rangeHeaderValue);
        }

        try {
            int responseCode = connection.getResponseCode();

            if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
                long fileSize = getRemoteFileSize(connection);
                return ResponseEntity.status(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
                        .header("Content-Range", "bytes */" + fileSize)
                        .body(null);
            }

            if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST) {
                return createErrorResponse(HttpStatus.BAD_REQUEST, "Invalid Range format");
            }

            if (responseCode == HttpURLConnection.HTTP_OK || responseCode == HttpURLConnection.HTTP_PARTIAL) {
                long fileSize = getRemoteFileSize(connection);
                String fileName = getFileNameFromUrl(fileUrl);
                String contentType = connection.getContentType();

                HttpHeaders headers = createDownloadHeaders(fileName, fileSize, start, end, hasRange);
                if (contentType != null) {
                    headers.set("Content-Type", contentType);
                }

                StreamingResponseBody responseBody = outputStream -> {
                    try (InputStream inputStream = connection.getInputStream()) {
                        long contentLength = connection.getContentLengthLong();
                        streamRemoteFile(inputStream, outputStream, request, contentLength);
                    } catch (DownloadInterruptedException e) {
                        handleDownloadInterruption(outputStream, e.getBytesTransferred(), e.getMessage());
                    }
                };

                return new ResponseEntity<>(responseBody, headers,
                        responseCode == HttpURLConnection.HTTP_PARTIAL ? HttpStatus.PARTIAL_CONTENT : HttpStatus.OK);
            }

            return createErrorResponse(HttpStatus.valueOf(responseCode),
                    "远程文件访问失败: " + connection.getResponseMessage());

        } finally {
            connection.disconnect();
        }
    }

    /**
     * 创建下载响应头
     */
    private HttpHeaders createDownloadHeaders(String fileName, long fileSize, long start, long end, boolean hasRange) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/octet-stream");
        headers.set("Content-Disposition", "attachment; filename=\"" + getSafeFileName(fileName) + "\"");
        headers.set("Accept-Ranges", "bytes");
        headers.set("Cache-Control", "no-cache");

        if (hasRange) {
            headers.set("Content-Range", "bytes " + start + "-" + end + "/" + fileSize);
            headers.set("Content-Length", String.valueOf(end - start + 1));
        } else {
            headers.set("Content-Length", String.valueOf(fileSize));
        }

        return headers;
    }

    /**
     * 流式传输文件范围（保持断点续传功能）
     */
    private void streamFileRange(RandomAccessFile randomFile, OutputStream outputStream,
                                 long start, long end, HttpServletRequest request) throws IOException {
        long contentLength = end - start + 1;
        randomFile.seek(start);

        byte[] buffer = new byte[4096];
        long remaining = contentLength;
        long bytesTransferred = 0;

        while (remaining > 0) {
            int bytesToRead = (int) Math.min(buffer.length, remaining);
            int bytesRead = randomFile.read(buffer, 0, bytesToRead);
            if (bytesRead == -1) break;

            outputStream.write(buffer, 0, bytesRead);
            remaining -= bytesRead;
            bytesTransferred += bytesRead;

            // 检查客户端连接
            if (!isClientConnected(request)) {
                throw new DownloadInterruptedException(bytesTransferred, "客户端连接中断");
            }
        }
        outputStream.flush();
    }

    /**
     * 流式传输完整文件
     */
    private void streamFullFile(RandomAccessFile randomFile, OutputStream outputStream,
                                HttpServletRequest request) throws IOException {
        long fileSize = randomFile.length();
        byte[] buffer = new byte[4096];
        long bytesTransferred = 0;
        int bytesRead;

        while ((bytesRead = randomFile.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
            bytesTransferred += bytesRead;

            // 检查客户端连接
            if (!isClientConnected(request)) {
                throw new DownloadInterruptedException(bytesTransferred, "客户端连接中断");
            }
        }
        outputStream.flush();
    }

    /**
     * 流式传输远程文件
     */
    private void streamRemoteFile(InputStream inputStream, OutputStream outputStream,
                                  HttpServletRequest request, long contentLength) throws IOException {
        byte[] buffer = new byte[4096];
        int bytesRead;
        long totalRead = 0;
        long bytesTransferred = 0;

        while (totalRead < contentLength && (bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
            totalRead += bytesRead;
            bytesTransferred += bytesRead;

            // 检查客户端连接
            if (!isClientConnected(request)) {
                throw new DownloadInterruptedException(bytesTransferred, "客户端连接中断");
            }
        }
        outputStream.flush();
    }

    /**
     * 处理下载中断
     */
    private void handleDownloadInterruption(OutputStream outputStream, long bytesTransferred, String message) {
        try {
            JSONObject jsonResponse = new JSONObject();
            jsonResponse.put("interrupted", true);
            jsonResponse.put("bytesTransferred", bytesTransferred);
            jsonResponse.put("resumePosition", bytesTransferred);
            jsonResponse.put("message", message);

            outputStream.write(jsonResponse.toString().getBytes(StandardCharsets.UTF_8));
            outputStream.flush();
        } catch (IOException e) {
            log.warn("下载中断信息发送失败", e);
        }
    }

    /**
     * 检查客户端是否仍然连接
     */
    private boolean isClientConnected(HttpServletRequest request) {
        try {
            // 对于异步请求
            if (request.isAsyncStarted()) {
                return request.getAsyncContext().getResponse() != null;
            }
            // 对于同步请求，尝试获取writer来检测连接
            try {
                request.getReader();
                return true;
            } catch (IllegalStateException e) {
                return false;
            }
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取文件大小
     */
    private long getFileSize(String fileUrl) throws IOException {
        if (isLocalFile(fileUrl)) {
            String filePath = fileUrl;
            if (!filePath.startsWith(localStoragePath)) {
                filePath = localStoragePath + File.separator + filePath;
            }
            File file = new File(filePath);
            return file.exists() ? file.length() : -1;
        } else if (isHttpUrl(fileUrl)) {
            URL url = new URL(fileUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("HEAD");
            try {
                return connection.getContentLengthLong();
            } finally {
                connection.disconnect();
            }
        }
        return -1;
    }

    /**
     * 获取远程文件大小
     */
    private long getRemoteFileSize(HttpURLConnection connection) {
        try {
            // 从Content-Range头获取文件大小
            String contentRange = connection.getHeaderField("Content-Range");
            if (contentRange != null && contentRange.contains("/")) {
                String totalSizeStr = contentRange.split("/")[1];
                return Long.parseLong(totalSizeStr);
            }
            // 从Content-Length获取
            return connection.getContentLengthLong();
        } catch (NumberFormatException e) {
            return -1;
        }
    }

    /**
     * 从URL获取文件名
     */
    private String getFileNameFromUrl(String fileUrl) {
        try {
            URL url = new URL(fileUrl);
            String path = url.getPath();
            return path.substring(path.lastIndexOf('/') + 1);
        } catch (Exception e) {
            return "download.bin";
        }
    }

    /**
     * 确保文件名安全
     */
    private String getSafeFileName(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return "download.bin";
        }
        // 移除路径信息
        String safeName = fileName;
        if (safeName.contains("/")) {
            safeName = safeName.substring(safeName.lastIndexOf("/") + 1);
        }
        if (safeName.contains("\\")) {
            safeName = safeName.substring(safeName.lastIndexOf("\\") + 1);
        }
        // 移除非法字符
        safeName = safeName.replaceAll("[\\\\/:*?\"<>|]", "_");
        return StringUtils.isBlank(safeName) ? "download.bin" : safeName;
    }

    /**
     * 判断是否为本地文件
     */
    private boolean isLocalFile(String fileUrl) {
        return !fileUrl.startsWith("http://") && !fileUrl.startsWith("https://") && !fileUrl.contains("://");
    }

    /**
     * 判断是否为HTTP/HTTPS URL
     */
    private boolean isHttpUrl(String fileUrl) {
        return fileUrl.startsWith("http://") || fileUrl.startsWith("https://");
    }


}
